﻿namespace AjLogo
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;

    using AjLogo.Language;
    using AjLogo.Primitives;

    public class Machine
    {
        private BindingEnvironment values;

        private Dictionary<string, IProcedure> procedures = new Dictionary<string, IProcedure>();

        private Dictionary<string, object> propertylists = new Dictionary<string, object>();

        public Machine()
        {
            this.values = new BindingEnvironment(this);
            this.Output = System.Console.Out;

            this.SetProcedure("make", new Make());
            this.SetProcedure("thing", new Thing());
            this.SetProcedure("word", new Word());
            this.SetProcedure("list", new List());
            this.SetProcedure("print", new Print());
            this.SetProcedure("isnumber", new IsNumber());
            this.SetProcedure("isword", new IsWord());
            this.SetProcedure("islist", new IsList());
            this.SetProcedure("cons", new Cons());
            this.SetProcedure("first", new First());
            this.SetProcedure("rest", new Rest());
        }

        public TextWriter Output { get; set; }

        public object GetValue(string name)
        {
            return this.values.GetValue(name);
        }

        public IProcedure GetProcedure(string name)
        {
            string key = NormalizeKey(name);

            if (this.procedures.ContainsKey(key))
            {
                return this.procedures[key];
            }

            return null;
        }

        public void SetProcedure(string name, IProcedure procedure)
        {
            this.procedures[NormalizeKey(name)] = procedure;
        }

        public object GetPropertyList(string name)
        {
            string key = NormalizeKey(name);

            if (this.propertylists.ContainsKey(key))
            {
                return this.propertylists[key];
            }

            return null;
        }

        public void SetValue(string name, object value)
        {
            this.values.SetValue(name, value);
        }

        internal static string NormalizeKey(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(key);
            }

            return key.ToUpperInvariant();
        }
    }
}

